package ssi.staaar.control;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ConnectException;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.ArrayList;
import java.util.Date;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

import org.json.me.JSONException;
import org.json.me.JSONObject;

import ssi.peeno.messages.highlevel.FriendAccept;
import ssi.peeno.messages.highlevel.FriendAcceptResponse;
import ssi.peeno.messages.highlevel.FriendListRequest;
import ssi.peeno.messages.highlevel.FriendListResponse;
import ssi.peeno.messages.highlevel.FriendRemove;
import ssi.peeno.messages.highlevel.FriendRequest;
import ssi.peeno.messages.highlevel.FriendSearchRequest;
import ssi.peeno.messages.highlevel.FriendSearchResponse;
import ssi.peeno.messages.highlevel.HighLevelMessage;
import ssi.peeno.messages.highlevel.LogoutMessage;
import ssi.peeno.messages.highlevel.MessageAdd;
import ssi.peeno.messages.highlevel.MessageListRequest;
import ssi.peeno.messages.highlevel.MessageListResponse;
import ssi.peeno.messages.highlevel.MessageTextRequest;
import ssi.peeno.messages.highlevel.MessageTextResponse;
import ssi.peeno.messages.highlevel.NotifyRequest;
import ssi.peeno.messages.highlevel.NotifyResponse;
import ssi.peeno.model.Message;
import ssi.peeno.model.User;
import ssi.staaar.ciphers.AsymCipher;
import ssi.staaar.ciphers.SHA1Digester;
import ssi.staaar.ciphers.SymCipher;
import ssi.staaar.exceptions.ServerException;
import ssi.staaar.managers.KeyManager;
import ssi.staaar.messages.KeyRecoveryMessage;
import ssi.staaar.messages.KeyRecoveryResponseMessage;
import ssi.staaar.messages.LowMessage;
import ssi.staaar.messages.RegMessage;
import ssi.staaar.messages.RequestParser;
import ssi.staaar.messages.SynAckMessage;
import ssi.staaar.messages.SynMessage;
import ssi.staaar.util.Javabean2JSON;
import android.content.Context;
import android.util.Log;
import encoders.Base64;

public class StaaarControl {
	private String serverAddress;
	private int port;
	private String keyPwd;
	private static KeyManager km;
	private AsymCipher asymcipher;
	private SymCipher sessionCipher;
	private RequestParser parser;
	private long timestamp;
	private User user;
	private Socket sck;
	private PrintWriter out;
	private BufferedReader input;
	private boolean activeSession = false;
	private SecretKey sessionKey;
	private int sessionId;
	private int msgCount = 0;
	
	public boolean isActiveSession() {
		return activeSession;
	}
	
	public void setActiveSession(boolean activeSession) {
		this.activeSession = activeSession;
	}
	
	public SecretKey getSessionKey() {
		return sessionKey;
	}
	
	public void setSessionKey(SecretKey sessionKey) {
		this.sessionKey = sessionKey;
	}
	
	public int getSessionId() {
		return sessionId;
	}
	
	public void setSessionId(int sessionId) {
		this.sessionId = sessionId;
	}
	
	public StaaarControl(Context context, String server, int port, String keyPwd) {
		serverAddress = server;
		this.port = port;
		this.keyPwd = keyPwd;
		km = new KeyManager(context, keyPwd);
		parser = new RequestParser();
	}

	public void acceptFriend(String username) {
		String inputLine = null;
		int lineLen = 0;
		char[] cryptedMsg;
		startAcceptFriendRequest(username);
		int sessionid;
		try {
			while ((inputLine = input.readLine()) != null) {
				if (inputLine.equals("MSG")) {
					Log.d("[FRIENDACCEPT]", "MSG RECEIVED");
					sessionid = Integer.parseInt(input.readLine());
					lineLen = Integer.parseInt(input.readLine());
					cryptedMsg = new char[lineLen];
					input.read(cryptedMsg, 0, lineLen);
					FriendAcceptResponse msg = (FriendAcceptResponse) onMsgReceived(new String(cryptedMsg));
					km.storeUserKey(msg.getUsername(), km.getKeyFromByteArray(msg.getPub().getBytes()));
					return;
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public void addUser(String username) {
		FriendRequest fr = new FriendRequest();
		fr.setFriendUserName(username);
		send(fr);
	}
	
	public void checkKeys(ArrayList<User> accepted) {
		ArrayList<String> usernames = new ArrayList<String>();
		for (int i=0; i<accepted.size(); ++i)
			usernames.add(accepted.get(i).getUsername() + ".public");
		km.checkKeys(usernames);
	}
	
	public void checkNotification() {
		NotifyRequest req = new NotifyRequest();
		send(req);
		new Thread(new Runnable() {
	        public void run() {
	        	String inputLine=null;
	        	int lineLen;
	        	char[] cryptedMsg;
	        	int sessionid;
	    		try {
	    			while ((inputLine = input.readLine()) != null) {
	    				if (inputLine.equals("MSG")) {
	    					Log.d("[MSGLIST]", "MSG RECEIVED");
	    					sessionid=Integer.parseInt(input.readLine());
	    					Log.d("[MSGLIST]", "SESSION ID" +sessionid);
	    					lineLen = Integer.parseInt(input.readLine());
	    					Log.d("[MSGLIST]", "LINELEN" +lineLen);
	    					cryptedMsg = new char[lineLen];
	    					int c = input.read(cryptedMsg, 0, lineLen);
	    					Log.d("[MSGLIST]", " " + c + "==" + new String(cryptedMsg).length());
	    					while (c< lineLen){
	    						c += input.read(cryptedMsg, c, lineLen-c);
	    						Log.d("[MSGLIST]", " " + c + "==" + new String(cryptedMsg).length());
	    					}
	    					Log.d("[MSGLIST]", " " + c + "==" + new String(cryptedMsg).length());
	    					NotifyResponse msg = (NotifyResponse) onMsgReceived(new String(cryptedMsg));
	    					onNotificationReceived(msg);
	    					return;
	    				}
	    			}
	    		} catch (Exception e) {
	    			e.printStackTrace();
	    			
	    		}
	        }
	       }		
		).start(); 
		
	}

	private <T> ArrayList<T> construcArrayList(T[] obj) {
		ArrayList<T> user = new ArrayList<T>();
		for (int i=0; i<obj.length; ++i)
			user.add(obj[i]);
		return user;
	}

	public String decryptMessage(String author, String ciphertext) {
		AsymCipher asym = new AsymCipher(author);
		asym.setPublicKey(km.loadUserKey(author));
		try {
			return asym.decryptWithPublic(ciphertext);
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	public String decryptMyMessage(String ciphertext) {
		AsymCipher asym = new AsymCipher();
		asym.setPublicKey(km.loadMyPublic());
		try {
			return asym.decryptWithPublic(ciphertext);
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	public String decryptPrivateMessage(String ciphertext) {
		AsymCipher asym = new AsymCipher();
		asym.setPrivateKey(km.loadMyPrivate());
		try {
			return asym.decryptWithPrivate(ciphertext);
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	private boolean establishSession() {
		AsymCipher serverCipher = new AsymCipher("Server");
		PublicKey k = km.loadUserKey("Server");
		serverCipher.setPublicKey(k);
		SynMessage synmsg = new SynMessage();
		synmsg.setUsername(user.getUsername());
		synmsg.setPassword(SHA1Digester.digestString(user.getPassword()));
		asymcipher = new AsymCipher(user.getUsername());
		asymcipher.setPrivateKey(km.loadMyPrivate());
		timestamp = new Date().getTime();
		synmsg.setTimeStamp(timestamp);
		try {
			String output = serverCipher.cryptWithPublicKey(synmsg.toString());
			Log.d("[LOGIN]", "sending syn...");
			out.println("SYN");
			out.println(output.length());
			out.print(output);
			out.flush();
			String inputLine;
			while ((inputLine = input.readLine()) != null) {
				if (inputLine.equals("SYN/ACK")) {
					String lineLen = input.readLine();
					int len = Integer.parseInt(lineLen);
					char[] msg = new char[len];
					input.read(msg, 0, len);
					String decMsg = asymcipher.decryptWithPrivate(new String(msg));
					Log.d("[LOGIN]", "[after dec]" + decMsg);
					//onsynack imposta la chiave l'id e il timestamp da mandare al server torna un synackmsg da rimandare al server
					SynAckMessage retmsg = onSynAckReceived(new String(decMsg));
					sessionCipher = new SymCipher();
					sessionCipher.setKey(sessionKey);
					String msgToReturn = sessionCipher.crypt(retmsg.toString());
					Log.d("[LOGIN]", "[synack]" + retmsg.toString());
					out.println("SYN/ACK");
					out.println(sessionId);
					out.println(msgToReturn.length());
					out.print(msgToReturn);
					out.flush();
					activeSession = true;
					Log.d("[LOGIN]", "Session established, sessionID: "
							+ sessionId);
					return true;
										
				} else if (inputLine.equals("403")) {
					Log.d("[LOGIN]", "Username o passwd sbagliati");
					return false;
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
		return false;
	}

	public void finishConnection() {
		out.close();
		try {
			input.close();
			sck.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public int getMsgCount() {
		return msgCount;
	}

	public Object getUser() {
		return user;
	}

	public boolean login(String username, String password) {
		user = new User(username, password);
		return establishSession();
	}
	
	public void logout() {
		LogoutMessage req = new LogoutMessage();
		send(req);
	}

	private HighLevelMessage onMsgReceived(String string) {
		String lmsgs = null;
		try {
			lmsgs = sessionCipher.decrypt(string);
		} catch (Exception e) {
			Log.d("[JSONERR]", string);
			e.printStackTrace();
		}
		LowMessage lmsg = parser.parseMsgMessage(lmsgs);
		try {
			HighLevelMessage msg = (HighLevelMessage) Javabean2JSON
					.fromJSONObject(new JSONObject(
							new String(lmsg.getPayload())));
			return msg;
		} catch (JSONException e) {
			e.printStackTrace();
			Log.d("[JSONERR]", "ON MSG RECEIVED FINISHED ON CATCH");
			Log.d("[JSONERR]", lmsgs);
			return null;
		}		
	}

	/**
	 * Scroll all of the notification message list
	 * TODO maybe some notification to user? (notification of accepted friends are already in managefriends) 
	 * @param msg
	 */
	protected void onNotificationReceived(NotifyResponse msg) {
		HighLevelMessage[] notificationList = msg.getNotifyList();
		FriendAcceptResponse far;
		if (notificationList == null)
			return;
		for (int i=0; i<notificationList.length; i++) {
			if (notificationList[i] instanceof FriendAcceptResponse) {
				far = (FriendAcceptResponse) notificationList[i];
				try {
					km.storeUserKey(far.getUsername(), AsymCipher.getPubKeyFromString(far.getPub()));
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
	}

	private boolean onRecAckReceived(String string, String plainPwd) {
		KeyRecoveryResponseMessage msg = parser.parseRecoveryMessage(string);
		Log.d("REC/ACK", timestamp + "==" + msg.getTimestamp() + "+1");
		if (msg.getTimestamp() == (timestamp + 1)) {
			Log.d("REC/ACK", "qui si");
			try {
				SymCipher sym = new SymCipher();
				sym.setPasswordAsKey(plainPwd);
				PublicKey pub = AsymCipher.getPubKeyFromString(msg.getPubKey());
				String privDec = sym.decrypt(msg.getPrivKey());
				PrivateKey priv = AsymCipher.getPrivKeyFromString(privDec);
				km.storeMyPrivate(priv);
				km.storeMyPublic(pub);
				return true;
			} catch (Exception e) {
				e.printStackTrace();
				return false;
			}
		} else {
			return false;
		}
	}

	/**
	 * Callback for REG/ACK message received
	 * - check if timestamp read in the message before from server is correct 
	 * (othrewise server is not trusted, someone has tried to guess the timestamp)
	 */
	private boolean onRegAckReceived(long ts) {
		if (ts == timestamp + 1) {
			out.println("REG/ACK/R");			
			km.storeMyPublic(asymcipher.getPublicKey());
			km.storeMyPrivate(asymcipher.getPrivateKey());
			return true;
		} else {
			out.println("REG/FAIL");
			return false;
		}
	}

	private SynAckMessage onSynAckReceived(String msg) {
		RequestParser rp = new RequestParser();
		SynAckMessage sackMsg = rp.parseSynAckMessage(msg);
		Log.d("[SYNACK]", "[onsynack]" + sackMsg.toString());
		//check if server timestamp is correct
		if (sackMsg.getTimestampClient() != timestamp) {
			Log.d("[SYNACK]", "Qui c'è qualcosa che non va il server non si è autenticato, meglio che stacchiamo. Ciao!");
			System.exit(-1);
		}
		sessionId = sackMsg.getSessionId();
		String stringKey = sackMsg.getSessionKey();
		byte[] keyenc = stringKey.getBytes();
		sessionKey = new SecretKeySpec(Base64.decode(keyenc), "AES");
		SynAckMessage lmsg = new SynAckMessage();
		lmsg.setTimestampServer(sackMsg.getTimestampServer());
		return lmsg;
	}

	public boolean recoveryKey(String username, String nonCryptedPwd) {
		String inputLine = null;
		KeyRecoveryMessage kmsg = new KeyRecoveryMessage();
		kmsg.setUsername(username);
		kmsg.setCryptedPwd(SHA1Digester.digestString(nonCryptedPwd));
		timestamp = (new Date()).getTime();
		kmsg.setTimestamp(timestamp);
		AsymCipher serverCipher = new AsymCipher("Server", keyPwd);
		PublicKey k = km.loadUserKey("Server");
		try {
			serverCipher.setPublicKey(k);
			String output = serverCipher.cryptWithPublicKey(kmsg.toString());
			Log.d("[REC]", "Sending...\n" + kmsg.toString());
			out.println("REC");
			out.println(output.length());
			out.print(output);
			out.flush();
		} catch (Exception e) {
			e.printStackTrace();
		}
		try {
			while ((inputLine = input.readLine()) != null) { 
				if (inputLine.equals("REC/ACK")) {
					Log.d("[RECACK]", "REC/ACK RECEIVED");
					try {
						String line = input.readLine();
						int lineLen = Integer.parseInt(line);
						char[] msg = new char[lineLen];
						int bytecount = input.read(msg,0, lineLen);
						while (bytecount < lineLen) {
							bytecount = input.read(msg,bytecount, lineLen-bytecount);
						}
						return onRecAckReceived(new String(msg), nonCryptedPwd);
					} catch (Exception e) {
						e.printStackTrace();
					}
				} else if (inputLine.equals("REC/FAIL")) {
					Log.d("[RECACK]", "Key Recovery failed");
					break;
				} else {
					Log.d("[RECACK]", "Received unknown command: " + inputLine + "say WHAT? to server");
					out.println("WHAT?");
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		return false;
	}

	public void refuseFriend(String username) {
		FriendRemove rem = new FriendRemove();
		rem.setFriendUserName(username);
		rem.setRefuse(true);
		send(rem);
	}
	
	public boolean registrate(String username, String email, String password) {
		String inputLine = null;
		long tempInteger = 0;
		Log.d("[REG]", "Start registration...");
		startRegistration(username, email, password);
		/**
		 * Response cases:
		 * - REG/ACK server has understood the REG command and proceed with registration
		 * - REG/FAIL server don't want you to registrate (wrong credentials)
		 */
		try {
			while ((inputLine = input.readLine()) != null) { 
				if (inputLine.equals("REG/ACK")) {
					Log.d("[REGACK]", "REG/ACK RECEIVED");
					try {
						String line = input.readLine();
						int lineLen = Integer.parseInt(line);
						char[] msg = new char[222];
						input.read(msg,0, lineLen);
						String res = asymcipher.decryptWithPrivate(new String(msg));
						Log.d("[REGACK]", "Received: " + res);
						tempInteger = Long.parseLong(res);
						return onRegAckReceived(tempInteger);
					} catch (Exception e) {
						e.printStackTrace();
					}
				} else if (inputLine.equals("REG/FAIL")) {
					Log.d("[REGACK]", "Registration failed");
					break;
				} else {
					Log.d("[REGACK]", "Received unknown command: " + inputLine + "say WHAT? to server");
					out.println("WHAT?");
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		return false;
	}
	
	public void removeFriend(String username) {
		FriendRemove rem = new FriendRemove();
		rem.setFriendUserName(username);
		rem.setRefuse(false);
		send(rem);
		km.removeUserKey(username);
	}
	
	public ArrayList<User> requestFriendList(int what) {
		String inputLine = null;
		int lineLen = 0;
		char[] cryptedMsg;
		startRequestFriendList(what);
		int sessionid;
		try {
			while ((inputLine = input.readLine()) != null) {
				if (inputLine.equals("MSG")) {
					Log.d("[REQUESTFRIENDLIST]", "MSG RECEIVED");
					sessionid=Integer.parseInt(input.readLine());
					lineLen = Integer.parseInt(input.readLine());
					cryptedMsg = new char[lineLen];
					input.read(cryptedMsg, 0, lineLen);
					FriendListResponse msg = (FriendListResponse) onMsgReceived(new String(cryptedMsg));
					return construcArrayList(msg.getUsers());
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
		return null;
	}
	
	public ArrayList<Message> requestMessageList(double latitude, double longitude,
			float radiusMeters, int what) {
		MessageListRequest req = new MessageListRequest();
		req.setLat(latitude);
		req.setLon(longitude);
		req.setRadius(radiusMeters);
		req.setWhat(what);
		send(req);
		String inputLine = null;
		int lineLen = 0;
		char[] cryptedMsg;
		int sessionid;
		try {
			while ((inputLine = input.readLine()) != null) {
				if (inputLine.equals("MSG")) {
					Log.d("[MSGLIST]", "MSG RECEIVED");
					sessionid=Integer.parseInt(input.readLine());
					Log.d("[MSGLIST]", "SESSION ID" +sessionid);
					lineLen = Integer.parseInt(input.readLine());
					Log.d("[MSGLIST]", "LINELEN" +lineLen);				
					cryptedMsg = new char[lineLen];
					int c = input.read(cryptedMsg, 0, lineLen);
					Log.d("[MSGLIST]", " " + c + "==" + new String(cryptedMsg).length());
					while (c< lineLen){
						c +=input.read(cryptedMsg, c, lineLen-c);
						Log.d("[MSGLIST]", " " + c + "==" + new String(cryptedMsg).length());
					}
					Log.d("[MSGLIST]", " " + c + "==" + new String(cryptedMsg).length());
					MessageListResponse msg = (MessageListResponse) onMsgReceived(new String(cryptedMsg));
					return construcArrayList(msg.getMessages());
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
		return null;
	}

	public boolean restoreSession(int sId, String sKey, User u) {
		sessionId=sId;
		sessionKey=SymCipher.stringToKey(sKey);
		sessionCipher = new SymCipher();
		sessionCipher.setKey(sKey);
		sessionCipher.setSessionId(sId);
		activeSession=true;
		user = u;
		return true;
	}

	public String retrieveCompleteMessage(int id) {
		MessageTextRequest req = new MessageTextRequest();
		req.setId(id);
		send(req);
		String inputLine = null;
		int lineLen = 0;
		char[] cryptedMsg;
		int sessionid;
		try {
			while ((inputLine = input.readLine()) != null) {
				if (inputLine.equals("MSG")) {
					Log.d("[MSGLIST]", "MSG RECEIVED");
					sessionid= Integer.parseInt(input.readLine());
					lineLen = Integer.parseInt(input.readLine());
					cryptedMsg = new char[lineLen];
					input.read(cryptedMsg, 0, lineLen);
					MessageTextResponse msg = (MessageTextResponse) onMsgReceived(new String(cryptedMsg));
					return msg.getText();
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
		return null;
	}

	public User[] searchUser(String c) {
		String inputLine = null;
		int lineLen = 0;
		char[] cryptedMsg;
		User[] users = null;
		startSearchUser(c);
		int sessionid;
		try {
			while ((inputLine = input.readLine()) != null) {
				if (inputLine.equals("MSG")) {
					Log.d("[SEARCHUSERMSG]", "MSG RECEIVED");
					sessionid=Integer.parseInt(input.readLine());
					lineLen = Integer.parseInt(input.readLine());
					cryptedMsg = new char[lineLen];
					input.read(cryptedMsg, 0, lineLen);
					FriendSearchResponse msg = (FriendSearchResponse) onMsgReceived(new String(cryptedMsg));
					return msg.getUser();
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
		
		return users;
	}
	
	public void send(HighLevelMessage msg) {
		LowMessage lmsg = new LowMessage();
		String ciphmsg = "";
		lmsg.setMsgId(msgCount);
		String payload = Javabean2JSON.toJSONObject(msg).toString();
		lmsg.setPayload(payload);
		out.println("MSG");
		out.println(sessionId);
		try {
			ciphmsg = sessionCipher.crypt(lmsg.toString());
		} catch (Exception e) {
			e.printStackTrace();
		}
		out.println(ciphmsg.length());
		out.print(ciphmsg);
		out.flush();
		if (out.checkError()){
			try {
				if (startConnection())
					send(msg);
			} catch (ServerException e) {
				e.printStackTrace();
			}
		}
	}

	public void sendMessage(String subject, String text, double latitude,
			double longitude, double altitude, String receiver, boolean crypt) {
		Log.d("ADD MESSAGE", "Sending message: " + subject);
		Log.d("ADD MESSAGE", "Message latitude: " + latitude);
		Log.d("ADD MESSAGE", "Message longitude: " + longitude);
		Log.d("ADD MESSAGE", "Message altitude: " + altitude);
		MessageAdd msg = new MessageAdd();
		msg.setSubject(subject);
		if (!receiver.equals("")) {
			AsymCipher cipher = new AsymCipher(receiver);
			cipher.setPublicKey(km.loadUserKey(receiver));
			try {
				msg.setText(cipher.cryptWithPublicKey(text));
			} catch (Exception e) {
				e.printStackTrace();
			}
			crypt = true;
		} else {
			if (crypt) {
				try {
					AsymCipher ciph = new AsymCipher();
					ciph.setPrivateKey(km.loadMyPrivate());
					msg.setText(ciph.cryptWithPrivateKey(text));
				} catch (Exception e) {
					e.printStackTrace();
				}
			} else
				msg.setText(text);
		}
		msg.setCrypt(crypt);
		msg.setReceiver(receiver);
		msg.setLat(latitude);
		msg.setLon(longitude);
		msg.setAlt(altitude);
		send(msg);
	}

	private void startAcceptFriendRequest(String username) {
		FriendAccept msg = new FriendAccept();
		msg.setFriendUserName(username);
		send(msg);
	}

	/**
	 * start registration handshake 
	 * - generate key
	 * - send REG message encrypted with server's key
	 */
	private void startRegistration(String username, String email, 
			String password) {
		user = new User(username, password);
		user.setEmail(email);
		asymcipher = new AsymCipher("Client");
		try {
			asymcipher.generateKey();
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		} catch (NoSuchProviderException e) {
			e.printStackTrace();
		}
		RegMessage regmsg = new RegMessage();
		regmsg.setEmail(email);
		regmsg.setPassword(password);
		regmsg.setUsername(username);
		regmsg.setPubKey((PublicKey) asymcipher.getPublicKey());
		regmsg.setPrivKey((PrivateKey) asymcipher.getPrivateKey());
		timestamp=new Date().getTime();
		regmsg.setTimestamp(timestamp);
		AsymCipher serverCipher = new AsymCipher("Server", keyPwd);
		PublicKey k = km.loadUserKey("Server");
	
		try {
			serverCipher.setPublicKey(k);
			String output = serverCipher.cryptWithPublicKey(regmsg.toString());
			Log.d("[REG]", "Sending...\n" + regmsg.toString());
			out.println("REG");
			out.println(output.length());
			out.print(output);
			out.flush();
		} catch (Exception e) {
			e.printStackTrace();
		}	
	}
	
	private void startRequestFriendList(int what) {
		FriendListRequest req = new FriendListRequest();
		req.setUsername(user.getUsername());
		req.setWhat(what);
		send(req);
	}
	
	private void startSearchUser(String c) {
		FriendSearchRequest frs = new FriendSearchRequest();
		frs.setCriteria(c);
		send(frs);
	}

	public boolean startConnection() throws ServerException {
		try {
			sck = new Socket(serverAddress, port);
			out = new PrintWriter(sck.getOutputStream(), true);
			input = new BufferedReader(new InputStreamReader(sck.getInputStream()));
			return true;
		} catch (ConnectException e) {
			throw new ServerException(e.getMessage());
		} catch (UnknownHostException e) {
			throw new ServerException(e.getMessage());
		} catch (IOException e) {
			throw new ServerException(e.getMessage());
		}
	}
}
